/*
 * Copyright 2017 Baiyyy
 * @(#)IInstruction.java
 */
package com.byyy.ccts.modules.cc.handle.istr;

import com.byyy.ccts.core.base.ILogBase;
import com.byyy.ccts.core.message.rocketmq.OAProducer;
import com.byyy.ccts.core.utils.*;
import com.byyy.ccts.modules.cc.Global;
import com.byyy.ccts.modules.cc.entity.ccts.*;
import com.byyy.ccts.modules.cc.entity.istr.InstructionMdl;
import com.byyy.ccts.modules.cc.enums.DeviceExecStateEnum;
import com.byyy.ccts.modules.cc.mapper.DeviceWarningLogMapper;
import com.byyy.ccts.modules.cc.service.*;
import com.byyy.ccts.modules.sys.entity.Log;
import com.byyy.ccts.modules.sys.service.IParamService;
import com.byyy.ccts.modules.sys.utils.UserUtils;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;

import java.sql.Timestamp;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 责任链模式处理指令抽象类
 * @author 李永波
 */
public abstract class IHandler implements ILogBase {

    /** 下一个处理对象 */
    private IHandler nextHandle;

    /** 设备Service */
    @Autowired
    private IDeviceService deviceService;

    /** 设备发送指令Service */
    @Autowired
    private IDeviceOptCmdService deviceOptCmdService;

    /** 每个处理类能够解析的命令，处理类必须重写改命令 */
    private byte instruction;

    /** 温湿度服务类 */
    @Autowired
    private IHumitureService humitureService;

    /** 参数服务类 */
    @Autowired
    private IParamService paramService;

    @Autowired
    private IPsZcdMxService psZcdMxService;

    /** 温湿度DAO */
    @Autowired
    private ICurrentHumitureService currentHumitureService;

    @Autowired
    private DeviceWarningLogMapper deviceWarningLogMapper;

    /**
     * 为每一个责任链上的处理者注册下一个处理类
     * @param nextHandle
     */
    public void setNextHandle(IHandler nextHandle) {
        this.nextHandle = nextHandle;
    }

    /**
     * 为每一个责任链上的处理者注册能够处理的指令
     * @param instruction
     */
    public void setInstruction(byte instruction) {
        this.instruction = instruction;
    }

    /**
     * 责任链模式核心代码
     * 递归调用类，直到找到可以解析并处理指令的类
     * @param istrs
     */
    public byte[] handleInstruction(byte[] istrs) {
        byte[] instructions = istrs;
        byte[] ret = null;
        // 判断是否为可处理指令
        if (istrs[1] == instruction) {
            instructions = this.escapeDeviceIstr(istrs);
            // 校验
            if (!this.check(instructions)) {
                return null;
            }
            // 判断版本号，根据不同的版本号对应不同的处理对象
            ret = this.handle(instructions);
            if (ret != null) {
                return this.escapeServerResponse(ret);
            }
            return null;
        }
        // 不可处理指令转交给下一个责任者进行处理
        else {
            if (this.nextHandle != null) {
                return this.nextHandle.handleInstruction(instructions);
            }
            else {
                logger.debug("未找到可执行的指令");
            }
        }

        return null;
    }

    /**
     * 生成指令实体(共通)
     * @param instructions
     * @return
     */
    public InstructionMdl generateInstructionModel(byte[] instructions) {
        InstructionMdl istrModel = new InstructionMdl();
        byte[] deviceId = new byte[6];
        byte[] sequenceNum = new byte[7];
        // 设备号
        for (int i = 0; i < 6; i++) {
            deviceId[i] = instructions[2 + i];
        }
        istrModel.setDeviceId(deviceId);
        // 流水号
        for (int i = 0; i < 7; i++) {
            sequenceNum[i] = instructions[8 + i];
        }
        istrModel.setSequenceNum(sequenceNum);
        return istrModel;
    }

    /**
     * 判断登录设备的版本号是否为V1.2，以后若版本扩展，可以定义为枚举类型
     * @return
     */
    public boolean isV12(byte[] instructions) {
        byte[] deviceId = new byte[6];
        // 设备号
        for (int i = 0; i < 6; i++) {
            deviceId[i] = instructions[2 + i];
        }
        List<TADevice> deviceList = (List) CacheUtils.get(Global.KEY_DEVICE_LIST);
        // 缓存中没有就从数据库获取，然后放入缓存
        if (deviceList == null || deviceList.size() == 0) {
            deviceList = this.deviceService.selectList(null);
            CacheUtils.put(Global.KEY_DEVICE_LIST, deviceList);
        }
        // 轮询ID，对比版本号
        String device = HexUtils.bytesToHexFun3(deviceId);
        for (TADevice temp : deviceList) {
            if (temp.getSn().equals(device)) {
                if (Global.VER_1_2.equals(temp.getVer())) {
                    return true;
                }
                else {
                    return false;
                }
            }
        }
        return false;
    }

    /**
     * 当解析完命令之后，进行下一步动作，目前不进行太过复杂的处理
     * 1.首先查询是否有该设备下的指令
     * 2.生成发送指令，一次只发送一条指令，如果有多条，按照时间顺序等待下一次设备连接时再发送
     * 3.进行服务器指令的转义
     * 4.更新指令状态
     * @param instructions
     */
    public byte[] afterHandle(byte[] instructions) {
        logger.debug("开始执行设备指令");
        // 查询是否有针对于该设备的指令
        InstructionMdl mdl = this.generateInstructionModel(instructions);
        List<TSDeviceOptCmd> cmds = this.deviceOptCmdService.getDeviceOptCmd(
                HexUtils.bytesToHexFun3(mdl.getDeviceId()), null, "1");
        if (cmds == null || cmds.size() < 1) {
            logger.debug("设备指令执行完毕(未找到指令)");
            return null;
        }

        TSDeviceOptCmd cmd = null;
        // 查询是否存在前置指令, 或者前置操作指令是否完成
        TSDeviceOptCmd preCmd = null;
        if (!StringUtils.isEmpty(cmds.get(0).getPreId())) {
            preCmd = this.deviceOptCmdService.selectById(cmds.get(0).getPreId());
        }
        if (preCmd != null) {
            if (DeviceExecStateEnum.等待执行.getValue().equals(preCmd.getExecState())) {
                cmd = preCmd;
            }
            else if (DeviceExecStateEnum.执行成功.getValue().equals(preCmd.getExecState())) {
                cmd = cmds.get(0);
            }
            else {
                logger.debug("设备指令执行完毕(前置指令{}正在执行或执行失败)", preCmd.getCmd());
                return null;
            }
        }
        else {
            cmd = cmds.get(0);
        }
        // 生成指令并发送
        byte optCmd = HexUtils.toBytes(cmd.getCmd())[0];
        byte[] ret = InstructionUtils.generateDeviceOptCmd(
                mdl, optCmd, cmd.getParam());

        // 更改指令状态(2:执行中),将服务器指令进行转义再发送
        byte[] sequenceNum = new byte[7];
        for (int i = 0; i < sequenceNum.length; i++) {
            sequenceNum[i] = ret[8 + i];
        }
        cmd.setSequenceNum(HexUtils.bytesToHexFun3(sequenceNum));
        cmd.setExecState("2");
        cmd.setExecTime(new Timestamp(System.currentTimeMillis()));
        this.deviceOptCmdService.updateById(cmd);

        logger.debug("设备指令执行完毕(指令:0x{}执行)", cmd.getCmd());
        return escapeServerResponse(ret);
    }

    /**
     * 每个实现类需要单独解析并处理指令
     * @param instructions
     * @return
     */
    public abstract byte[] handle(byte[] instructions);

    /**
     * 去除重复数据
     * @param deviceId
     * @param tsHumitures
     * @return
     */
    protected List<TSHumiture> clearRepeatData(String deviceId, List<TSHumiture> tsHumitures) {
        // 重复数据过滤
        List<String> collectionTimes = new ArrayList<>();
        for (TSHumiture temp : tsHumitures) {
            collectionTimes.add(DateUtils.formatDateTime(temp.getCollectTime()));
        }
        List<TSHumiture> repeatData = this.humitureService.queryRepeatData(deviceId, collectionTimes);

        if (repeatData == null || repeatData.size() < 1) {
            return tsHumitures;
        }

        // 去重
        boolean isRepeat = false;
        List<TSHumiture> noRepeatData = new ArrayList<>();
        for (TSHumiture all : tsHumitures) {
            isRepeat = false;
            for (TSHumiture repeat : repeatData) {
                if (DateUtils.getSecondOfTwoDate(all.getCollectTime(), repeat.getCollectTime()) == 0 ) {
                    isRepeat = true;
                    break;
                }
            }
            if (!isRepeat) {
                noRepeatData.add(all);
            }
        }
        return noRepeatData;
    }

    /**
     * 根据设备记录时间间隔，将温湿度数据存入数据库中
     * @param humitureList
     */
    protected void insertHumiture(List<TSHumiture> humitureList) {
        // 升序排序后将最新数据插入当前温度表中
        humitureList = humitureList.stream().sorted(Comparator.comparing(TSHumiture::getCollectTime))
                .collect(Collectors.toList());

        this.currentHumitureService.insertOrUpdate(convertHumiture(humitureList.get(humitureList.size() - 1)));

        // 获取数据库内存储的最新温度数据
        TSHumiture lastHumiture = null;
        String sn = humitureList.get(0).getDeviceSn();
        TADevice device = this.deviceService.selectBySn(sn);
        List<TSHumiture> recordHumitureList = new ArrayList<>();
        List<TSHumiture> lastedHumitureInfo = this.humitureService.queryLastedHumitureInfoNoLimit(Arrays.asList(device.getId()));
        int recordMinute = Integer.valueOf(device.getMinute());

        // 如果数据库内存在最新数据，赋值，不存在的话就吧当前列表第一个赋值
        if (lastedHumitureInfo != null && lastedHumitureInfo.size() > 0) {
            lastHumiture = lastedHumitureInfo.get(0);
        }
        else {
            lastHumiture = humitureList.get(0);
            recordHumitureList.add(lastHumiture);
        }

        for (TSHumiture tsHumiture : humitureList) {
            // 比较当前时间与数据库内最新时间，大于等于4分钟,记录温湿度数据，并以此为下一次比较的基准时间
            // 比较当前时间与数据库内最新时间，相差时间绝对值为4得倍数，记录该数据，防止补传数据上传落后与实时数据上传
            if (DateUtils.getSecondOfTwoDate(lastHumiture.getCollectTime(), tsHumiture.getCollectTime()) != 0
                    && ((Math.abs(DateUtils.getSecondOfTwoDate(lastHumiture.getCollectTime(), tsHumiture.getCollectTime())) / 60) % recordMinute) == 0) {
                recordHumitureList.add(tsHumiture);
            }
        }

        // 根据设备ID获取在途装车单，没有的话就返回
        PsZcdMx psZcdMx = this.psZcdMxService.selectMxOnTheWayBySN(sn);
        if (psZcdMx != null) {
            this.tempAlarm(device, psZcdMx, humitureList, recordHumitureList, lastHumiture);
            this.tempWarning(device, psZcdMx, recordHumitureList);
        }

        if (recordHumitureList != null && recordHumitureList.size() > 0) {
            humitureService.insertBatch(recordHumitureList);
        }

    }

    /**
     * 转换实体
     * @param humiture
     * @return
     */
    protected TSCurrentHumiture convertHumiture(TSHumiture humiture) {
        TSCurrentHumiture currentHumiture = new TSCurrentHumiture();
        currentHumiture.setDeviceSn(humiture.getDeviceSn());
        currentHumiture.setTemperature(humiture.getTemperature());
        currentHumiture.setCollectTime(humiture.getCollectTime());
        currentHumiture.setElectric(humiture.getElectric());
        currentHumiture.setHumidity(humiture.getHumidity());
        currentHumiture.setSemaphore(humiture.getSemaphore());
        currentHumiture.setCellId(humiture.getCellId());
        currentHumiture.setLac(humiture.getLac());

        return currentHumiture;
    }

    /**
     * 判断是否存在超温报警数据，报警数据针对于一分钟上传数据
     * 1.根据设备ID寻找在途装车单信息，按开始时间，结束时间(nulls first)排序，获取第一条数据
     * 2.获取预警范围
     * 3.判断是否存在预警信息以及报警信息
     * 4.预警报警信息插入数据库(异步)
     * 5.存在报警信息，则根据设备目前间隔时间(报警后设置为2分钟，恢复后设置为4分钟)
     * 6.发送邮件提醒(RocketMQ)
     * @param tsHumitures
     */
    private void tempAlarm(TADevice device, PsZcdMx psZcdMx, List<TSHumiture> tsHumitures, List<TSHumiture> recordHumitureList, TSHumiture lastHumiture) {
        String sn = device.getSn();
        logger.debug("开始设备[{}]在途单据报警处理", sn);
        float wendMin = 0f;
        float wendMax = 0f;
        // 温度报警相关值
        List<TSHumiture> alarmList = new ArrayList<>();
        // 判断上传温度时间是否在途配送单范围内
        Date beginDate = psZcdMx.getBeginDate();
        TemperatureMsgVo msgVo = null;
        List<TemperatureMsgVo> msgVoList = new ArrayList<>();
        StringBuilder smsInfo = new StringBuilder();

        // 判断是否存在报警以及预警信息
        wendMin = psZcdMx.getWendMin();
        wendMax = psZcdMx.getWendMax();

        // 循环判断是否存在超出商品温度预警界限以及报警界限
        for (TSHumiture humiture : tsHumitures) {
            // 设备上传温度时间 < 装车单开启时间；说明是无效数据
            if (humiture.getCollectTime().getTime() - beginDate.getTime() < 0) {
                continue;
            }

            // 超过报警信息
            if (humiture.getTemperature() > wendMax || humiture.getTemperature() < wendMin) {
                // 比较当前时间与数据库内最新时间，2分钟的倍数,记录报警的温湿度数据,否则就跳过
                if (DateUtils.getSecondOfTwoDate(lastHumiture.getCollectTime(), humiture.getCollectTime()) != 0
                        &&((Math.abs(DateUtils.getSecondOfTwoDate(lastHumiture.getCollectTime(), humiture.getCollectTime())) / 60) % 2) == 0) {
                    alarmList.add(humiture);
                    msgVo = new TemperatureMsgVo();
                    msgVo.setYwbm(psZcdMx.getYwbm());
                    msgVo.setDanwName(psZcdMx.getDanwName());
                    msgVo.setZcdNo(psZcdMx.getZhuangcdNo());
                    msgVo.setSbNo(device.getNo());
                    msgVo.setCollectTime(DateUtils.formatDate(humiture.getCollectTime(), Global.PATTERN_YYYYMMMDD_HHMMSS));
                    msgVo.setTemperature(humiture.getTemperature());
                    msgVo.setStandard(wendMin + "-" + wendMax);
                    msgVoList.add(msgVo);
                    smsInfo.append("装车单").append(psZcdMx.getZhuangcdNo()).append("内采集设备").append(device.getNo())
                            .append("于").append(DateUtils.formatDate(humiture.getCollectTime(), Global.PATTERN_YYYYMMMDD_HHMMSS))
                            .append("时温度为").append(humiture.getTemperature()).append(",标准温度为").append(wendMin).append("-")
                            .append(wendMax).append(";");

                }
            }
        }

        if (alarmList != null && alarmList.size() > 0) {
            FreeMarkerUtils utils = FreeMarkerUtils.initByDefaultTemplate();
            Map<String, List> map = new HashMap<>();
            map.put("list", msgVoList);
            String content = utils.processToString("temperatureMsg.ftl", map);

            OAProducer oAProducer = (OAProducer) SpringContextHolder.getBean("oAProducer");
            oAProducer.sendOAMailWithMsg("mailWarning" + psZcdMx.getCangku(), "冷链温度报警", content, smsInfo.toString());
            logger.debug("设备[{}]在途单据报警处理({})", sn, content);
        }

        // 判断报警信息是否已插入数据库，没有插入的话，就将报警信息放入插入队列中
        List<TSHumiture> toRecordList = new ArrayList<>();
        if (alarmList != null && alarmList.size() > 0) {
            if (recordHumitureList != null && recordHumitureList.size() > 0) {
                for (TSHumiture recordHumiture : recordHumitureList) {
                    for (TSHumiture alarmHumiture : alarmList) {
                        if (alarmHumiture.getCollectTime().compareTo(recordHumiture.getCollectTime()) != 0) {
                            toRecordList.add(alarmHumiture);
                        }
                    }
                }
            }
            else {
                toRecordList.addAll(alarmList);
            }
        }

        if (toRecordList != null && toRecordList.size() > 0) {
            recordHumitureList.addAll(toRecordList);
        }

        // 记录日志
        this.writeWarningLog(Log.TEMPERATURE_ALARM, psZcdMx.getZhuangcdNo(), device.getId(), alarmList);

        logger.debug("设备[{}]在途单据报警处理完毕", sn);
    }

    /**
     * 判断是否存在电量、温度预警信息，预警信息针对于4分钟存储一次的数据
     * 1.根据设备ID寻找在途装车单信息，按开始时间，结束时间(nulls first)排序，获取第一条数据
     * 2.获取预警范围
     * 3.判断是否存在预警信息以及报警信息
     * 4.预警报警信息插入数据库(异步)
     * 5.存在报警信息，则根据设备目前间隔时间(报警后设置为2分钟，恢复后设置为4分钟)
     * 6.发送邮件提醒(RocketMQ)
     * @param device
     * @param psZcdMx
     * @param recordHumitureList
     */
    private void tempWarning(TADevice device, PsZcdMx psZcdMx, List<TSHumiture> recordHumitureList) {
        String sn = device.getSn();
        logger.debug("开始设备[{}]在途单据预警处理", sn);
        float wendMin = 0f;
        float wendMax = 0f;
        // 温度预警相关值
        float top_warning = 0f;
        float lower_warning = 0f;
        // 温度报警相关值
        List<TSHumiture> warningList = new ArrayList<>();
        // 电量预警相关值
        TSHumiture electricWarning = null;
        boolean electricAlarmFlg = false;
        Float electricWarningValue = 0f;
        Float electricAlarmValue = 0f;
        StringBuilder electricWarningSb = new StringBuilder();
        // 判断上传温度时间是否在途配送单范围内
        Date beginDate = psZcdMx.getBeginDate();
        TemperatureMsgVo msgVo = null;
        List<TemperatureMsgVo> msgVoList = new ArrayList<>();

        // 判断是否存在报警以及预警信息
        wendMin = psZcdMx.getWendMin();
        wendMax = psZcdMx.getWendMax();

        // 预警下限值
        top_warning = wendMax - Global.FIXED_VALUE;
        // 预警上限值
        lower_warning = wendMin + Global.FIXED_VALUE;

        // 电量预警值
        electricWarningValue = this.paramService.getFloatValueByCode("ELECTRIC_WARNING");
        // 电量报警值
        electricAlarmValue = this.paramService.getFloatValueByCode("ELECTRIC_ALARM");

        // 循环判断是否存在超出商品温度预警界限以及报警界限
        for (TSHumiture humiture : recordHumitureList) {
            // 设备上传温度时间 < 装车单开启时间；说明是无效数据
            if (humiture.getCollectTime().getTime() - beginDate.getTime() < 0) {
                continue;
            }

            // 超过预警信息
            if (humiture.getTemperature() >= top_warning || humiture.getTemperature() <= lower_warning) {
                // 超过报警信息，上面已处理过，此处不在处理
                if (humiture.getTemperature() > wendMax || humiture.getTemperature() < wendMin) {
                }
                // 预警报警之间
                else {
                    warningList.add(humiture);
                    msgVo = new TemperatureMsgVo();
                    msgVo.setYwbm(psZcdMx.getYwbm());
                    msgVo.setDanwName(psZcdMx.getDanwName());
                    msgVo.setZcdNo(psZcdMx.getZhuangcdNo());
                    msgVo.setSbNo(device.getNo());
                    msgVo.setCollectTime(DateUtils.formatDate(humiture.getCollectTime(), Global.PATTERN_YYYYMMMDD_HHMMSS));
                    msgVo.setTemperature(humiture.getTemperature());
                    msgVo.setStandard(wendMin + "-" + wendMax);
                    msgVoList.add(msgVo);
                }
            }

            // 电量预警
            if (electricWarning == null) {
                if (humiture.getElectric() <= electricWarningValue) {
                    electricWarning = humiture;
                }
                if (humiture.getElectric() <= electricAlarmValue) {
                    electricAlarmFlg = true;
                }

            }
        }

        if(electricWarning != null) {
            // 判断是否预警过
            boolean warningExistsFlag = true;
            List<TSDeviceWarningLog> logList = this.deviceWarningLogMapper.queryWarning(device.getId(),
                    Log.ELECTRIC_WARNING, psZcdMx.getZhuangcdNo());
            // 如果存在两条，说明30和15的都报警过；如果一条不存在或只有一条，说明，只报警一次；
            if (electricAlarmFlg) {
                warningExistsFlag = (logList == null || logList.size() < 2) ? false : true;
                // 存在可能只报警了一条15电量的情况，此时判断保存的电量和当前电量差值不过5，默认已经报警完毕
                if (logList != null && logList.size() == 1) {
                    int value = Integer.valueOf(logList.get(0).getContent());
                    if (Math.abs(electricWarning.getElectric() - value) < 5) {
                        warningExistsFlag = true;
                    }
                }
            }
            else {
                warningExistsFlag = (logList == null || logList.size() < 1) ? false : true;
            }

            if (!warningExistsFlag) {
                // 记录日志
                this.writeWarningLog(Log.ELECTRIC_WARNING, psZcdMx. getZhuangcdNo(), device.getId(), Arrays.asList(electricWarning));
                // 发送邮件
                electricWarningSb.append("装车单").append(psZcdMx.getZhuangcdNo()).append("内采集设备").append(device.getNo())
                        .append("于").append(DateUtils.formatDate(electricWarning.getCollectTime(), Global.PATTERN_YYYYMMMDD_HHMMSS))
                        .append("时电量为").append(electricWarning.getElectric()).append("到达预警电量")
                        .append(electricAlarmFlg ? electricAlarmValue : electricWarningValue).append(",返回后及时充电！");
                OAProducer oAProducer = (OAProducer) SpringContextHolder.getBean("oAProducer");
                oAProducer.sendOAMailWithMsg("electricWarning" + psZcdMx.getCangku(), "设备电量预警", electricWarningSb.toString(), electricWarningSb.toString());
                logger.debug("设备[{}]在途单据电量预警处理({})", sn, electricWarningSb.toString());
            }

        }

        if (warningList != null && warningList.size() > 0) {
            FreeMarkerUtils utils = FreeMarkerUtils.initByDefaultTemplate();
            Map<String, List> map = new HashMap<>();
            map.put("list", msgVoList);
            String content = utils.processToString("temperatureMsg.ftl", map);

            this.writeWarningLog(Log.TEMPERATURE_WARNING, psZcdMx. getZhuangcdNo(), device.getId(), warningList);
            OAProducer oAProducer = (OAProducer) SpringContextHolder.getBean("oAProducer");
            oAProducer.sendOAMail("mailWarning" + psZcdMx.getCangku(), "冷链温度预警", content);
            logger.debug("设备[{}]在途单据预警处理({})", sn, content);
        }


        logger.debug("设备[{}]在途单据预警处理完毕", sn);
    }

    /**
     * 验证校验码
     * @param instructions
     * @return
     */
    private boolean check(byte[] instructions) {
        byte cmdChk = 0x00;
        for (int i = 0; i < instructions.length - 2; i++) {
            cmdChk ^= instructions[i];
        }

        if (Byte.compare(cmdChk, instructions[instructions.length - 2]) == 0) {
            logger.debug("校验成功");
            return true;
        }
        else {
            logger.debug("校验失败");
            return false;
        }
    }

    /**
     * 转义设备指令
     * 规则：
     *   0x7D->0x7D,0x01
     *   0x7E->0x7D,0x02
     *   0x1A->0x7D,0x03
     *   0x1B->0x7D,0x04
     * @param instructions
     */
    private byte[] escapeDeviceIstr(byte[] instructions) {
        logger.debug("设备数据转义前:{}", HexUtils.bytesToHexFun3(instructions, "0x%02x,"));
        List<Byte> istrList = new ArrayList<>();
        for (int i = 0; i < instructions.length - 1; i++) {
            if (instructions[i] == 0x7D) {
                switch (instructions[i + 1]) {
                    case 0x01:
                        istrList.add((byte) 0x7D);
                        i++;
                        continue;
                    case 0x02:
                        istrList.add((byte) 0x7E);
                        i++;
                        continue;
                    case 0x03:
                        istrList.add((byte) 0x1A);
                        i++;
                        continue;
                    case 0x04:
                        istrList.add((byte) 0x1B);
                        i++;
                        continue;
                    default:
                        break;
                }
            }
            istrList.add(instructions[i]);
        }
        istrList.add(instructions[instructions.length - 1]);

        // List->Array
        byte [] ret = new byte[istrList.size()];
        for (int i = 0; i < istrList.size(); i++) {
            ret[i] = istrList.get(i);
        }
        logger.debug("设备数据转义后:{}", HexUtils.bytesToHexFun3(ret, "0x%02x,"));
        return ret;
    }

    /**
     * 转义服务器指令
     * 规则：
     *   0x7E->0x7D,0x02
     *   0x7D->0x7D,0x01
     * @param instructions
     */
    private byte[] escapeServerResponse(byte[] instructions) {
        logger.debug("服务器数据转义前:{}", HexUtils.bytesToHexFun3(instructions, "0x%02x,"));
        List<Byte> istrList = new ArrayList<>();
        istrList.add(instructions[0]);
        for (int i = 1; i < instructions.length - 1; i++) {
                switch (instructions[i]) {
                    case 0x7D:
                        istrList.add((byte) 0x7D);
                        istrList.add((byte) 0x01);
                        continue;
                    case 0x7E:
                        istrList.add((byte) 0x7D);
                        istrList.add((byte) 0x02);
                        continue;
                    default:
                        break;
            }
            istrList.add(instructions[i]);
        }
        istrList.add(instructions[instructions.length - 1]);

        // List->Array
        byte [] ret = new byte[istrList.size()];
        for (int i = 0; i < istrList.size(); i++) {
            ret[i] = istrList.get(i);
        }
        logger.debug("服务器数据转义后:{}", HexUtils.bytesToHexFun3(ret, "0x%02x,"));
        return instructions;
    }

    /**
     * 写入日志
     * @param logType
     * @param zcdNo
     * @param deviceId
     * @param humitureList
     */
    private void writeWarningLog(String logType, String zcdNo, String deviceId, List<TSHumiture> humitureList) {
        TSDeviceWarningLog log = null;
        // 写入温度报警日志
        if (humitureList != null && humitureList.size() > 0) {
            for (TSHumiture temp : humitureList) {
                log = new TSDeviceWarningLog();
                log.setDeviceId(deviceId);
                log.setType(logType);
                log.setTitle(zcdNo);
                if (Log.ELECTRIC_WARNING.equals(logType)) {
                    log.setContent(String.valueOf(temp.getElectric()));
                }
                else {
                    log.setContent(String.valueOf(temp.getTemperature()));
                }
                log.setCollectTime(temp.getCollectTime());
                log.setCreateBy(UserUtils.getByUserName("admin"));
                log.setCreateDate(new Timestamp(System.currentTimeMillis()));
                this.deviceWarningLogMapper.insert(log);
            }

        }
    }

    @Data
    public static class TemperatureMsgVo {
        private String ywbm;
        private String danwName;
        private String zcdNo;
        private String sbNo;
        private String collectTime;
        private float temperature;
        private String standard;
    }

}
